package com.hk.commons.util;

import java.time.Instant;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;

/**
 * <p>
 * 高并发场景下System.currentTimeMillis()的性能问题的优化
 * </p>
 * <p>
 * System.currentTimeMillis()的调用比new一个普通对象要耗时的多（具体耗时高出多少我还没测试过，有人说是100倍左右）<br>
 * System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<br>
 * 后台定时更新时钟，JVM退出时，线程自动回收<br>
 * 10亿：43410,206,210.72815533980582%<br>
 * 1亿：4699,29,162.0344827586207%<br>
 * 1000万：480,12,40.0%<br>
 * 100万：50,10,5.0%<br>
 * </p>
 *
 * @author kevin
 * @date 2019-7-24 9:47
 * @see https://gitee.com/yu120/sequence
 */
public class SystemClock {

    private final LongAdder adder;

    private final ScheduledThreadPoolExecutor scheduler;

    private static class InstanceHolder {

        static final SystemClock INSTANCE = new SystemClock();
    }

    private SystemClock() {
        this.adder = new LongAdder();
        this.adder.add(Instant.now().toEpochMilli());
        this.scheduler = new ScheduledThreadPoolExecutor(1, runnable -> {
            var thread = new Thread(runnable, "System Clock");
            thread.setDaemon(true);
            return thread;
        });
        scheduleClockUpdating();
    }

    private static SystemClock instance() {
        return InstanceHolder.INSTANCE;
    }

    public static long now() {
        return instance().currentTimeMillis();
    }

    private void scheduleClockUpdating() {
        //每毫秒执行一次，毫秒数自增
        scheduler.scheduleAtFixedRate(adder::increment, 1, 1, TimeUnit.MILLISECONDS);
    }

    private long currentTimeMillis() {
        return adder.longValue();
    }

}
